﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
using JasLib.Extensions;

namespace JasLib.Interop.Win32
{
    public enum TaskDialogElements : int
    {
        Content,
        ExpandedInformation,
        Footer,
        MainInstruction,
    }

    public enum TaskDialogIconElement : int
    {
        Main,
        Footer,
    }

    public enum TaskDialogNotifications : uint
    {
        Created = 0,
        Navigated = 1,
        ButtonClicked = 2,            // wParam = Button ID
        HyperlinkClicked = 3,         // lParam = (LPCWSTR)pszHREF
        Timer = 4,                     // wParam = Milliseconds since dialog created or timer reset
        Destroyed = 5,
        RadioButtonClicked = 6,      // wParam = Radio Button ID
        DialogConstructed = 7,
        VerificationClicked = 8,      // wParam = 1 if checkbox checked, 0 if not, lParam is unused and always 0
        Help = 9,
        ExpandoButtonClicked = 10,    // wParam = 0 (dialog is now collapsed), wParam != 0 (dialog is now expanded)
    }

    public enum PBST
    {
        PBST_NORMAL = 0x0001,
        PBST_ERROR = 0x0002,
        PBST_PAUSED = 0x0003
    }

    public enum TaskDialogIcon : ushort
    {
        TD_WARNING_ICON = 65535,
        TD_ERROR_ICON = 65534,
        TD_INFORMATION_ICON = 65533,
        TD_SHIELD_ICON = 65532,
    }

    public static partial class COMCTL32
    {
        // Used in the various SET_DEFAULT* TaskDialog messages
        public const int NO_DEFAULT_BUTTON_SPECIFIED = 0;

        public const int TASKDIALOG_IDEALWIDTH = 0;  // Value for TASKDIALOGCONFIG.cxWidth
        public const int TASKDIALOG_BUTTON_SHIELD_ICON = 1;

        [DllImport(Constants.ModuleNames.COMCTL32, CharSet = CharSet.Auto)]
        public static extern HRESULT DllGetVersion(out DLLVERSIONINFO pdvi);

        [DllImport(Constants.ModuleNames.COMCTL32, CharSet = CharSet.Auto)]
        public static extern HRESULT DllGetVersion(out DLLVERSIONINFO2 pdvi);

        /// <summary>
        /// This is an enhanced MessageBox found only in Windows Vista or later.
        /// http://msdn.microsoft.com/en-us/library/bb760540(VS.85).aspx
        /// </summary>
        [DllImport(Constants.ModuleNames.COMCTL32, EntryPoint = "TaskDialog", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern HRESULT TaskDialog(
            HWND hParentWindow,
            IntPtr hInstance,
            string strTitle,
            string strMainInstruction,
            string strContent,
            TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons,
            IntPtr eIcon,
            ref DialogResultType iResult);

        /// <summary>
        /// Convenient wrapper for the Win32 TaskDialog function that throws an exception on failure.
        /// </summary>
        public static DialogResultType TaskDialog(
            HWND hWnd,
            string strTitle,
            string strMainInstruction,
            string strContent,
            TASKDIALOG_COMMON_BUTTON_FLAGS dwCommonButtons,
            TaskDialogIcon eIcon)
        {
            DialogResultType eDialogResult = 0;
            HRESULT hResult = TaskDialog(hWnd, IntPtr.Zero, strTitle, strMainInstruction, strContent, dwCommonButtons, (IntPtr)eIcon, ref eDialogResult);
            if (hResult.FAILED())
                throw hResult.ToCOMException("TaskDialog() failed.");
            return eDialogResult;
        }

        [DllImport(Constants.ModuleNames.COMCTL32, EntryPoint = "TaskDialogIndirect", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.Winapi, SetLastError = true)]
        public static extern HRESULT TaskDialogIndirect(
             [In] ref TASKDIALOGCONFIG.Native pTaskConfig,
             [Out] out int pnButton,
             [Out] out int pnRadioButton,
             [MarshalAs(UnmanagedType.Bool)][Out] out bool pVerificationFlagChecked);

#if DOT_NET_4_OR_LATER
        /// <summary>
        /// The pathetic CLR marshaller handles pointers to arrays in method parameters but not in struct fields. So we have to overload TaskDialogIndirect to remove the worst of the workarounds.
        /// </summary>
        /// <param name="pTaskConfig"></param>
        /// <param name="buttons">When specified, this function will marshal these buttons and set TASKDIALOGCONFIG.pButtons and TASKDIALOGCONFIG.cButtons accordingly.</param>
        /// <param name="radioButtons">When specified, this function will marshal these radio buttons and set TASKDIALOGCONFIG.pRadioButtons and TASKDIALOGCONFIG.cRadioButtons accordingly.</param>
        /// <returns></returns>
        public static Tuple<int, int, bool> TaskDialogIndirect(
            ref TASKDIALOGCONFIG.Managed taskConfig)
        {
            /// The return values, which will be combined into the returned tuple.
            int nButton;
            int nRadioButton;
            bool bVerificationFlagChecked;

            using (NativeHeapAllocationGroup allocGroup = new NativeHeapAllocationGroup())
            {
                TASKDIALOGCONFIG.Native pTaskConfig = new TASKDIALOGCONFIG.Native
                {
                    /// We take total control of these fields.
                    cbSize = MarshalHelper.SizeOf<TASKDIALOGCONFIG.Native>(),
                    pButtons = IntPtr.Zero,
                    cButtons = 0,
                    pRadioButtons = IntPtr.Zero,
                    cRadioButtons = 0,

                    hwndParent = taskConfig.hwndParent,
                    hInstance = taskConfig.hInstance,
                    dwFlags = taskConfig.dwFlags,
                    dwCommonButtons = taskConfig.dwCommonButtons,
                    pszWindowTitle = taskConfig.pszWindowTitle,
                    MainIcon = taskConfig.MainIcon,
                    pszMainInstruction = taskConfig.pszMainInstruction,
                    pszContent = taskConfig.pszContent,
                    nDefaultButton = taskConfig.nDefaultButton,
                    nDefaultRadioButton = taskConfig.nDefaultRadioButton,
                    pszVerificationText = taskConfig.pszVerificationText,
                    pszExpandedInformation = taskConfig.pszExpandedInformation,
                    pszExpandedControlText = taskConfig.pszExpandedControlText,
                    pszCollapsedControlText = taskConfig.pszCollapsedControlText,
                    //TASKDIALOGCONFIG_ICON_UNION FooterIcon;  // NOTE: 32-bit union field, holds pszFooterIcon as well
                    pszFooter = taskConfig.pszFooter,
                    //lpCallbackData = taskConfig.lpC
                    cxWidth = taskConfig.cxWidth,
                };

                if (taskConfig.pfCallback != null)
                    pTaskConfig.pfCallback = allocGroup.LockDelegate(taskConfig.pfCallback);

                try
                {
                    if (taskConfig.pButtons != null)
                    {
                        IList<TASKDIALOG_BUTTON> buttonList = taskConfig.pButtons.AsList();
                        pTaskConfig.pButtons = allocGroup.AllocArrayCopy(buttonList);
                        pTaskConfig.cButtons = (uint)buttonList.Count;
                    }

                    if (taskConfig.pRadioButtons != null)
                    {
                        IList<TASKDIALOG_BUTTON> radioButtonList = taskConfig.pRadioButtons.AsList();
                        pTaskConfig.pRadioButtons = allocGroup.AllocArrayCopy(radioButtonList);
                        pTaskConfig.cRadioButtons = (uint)radioButtonList.Count;
                    }

                    HRESULT hResult = TaskDialogIndirect(ref pTaskConfig, out nButton, out nRadioButton, out bVerificationFlagChecked);
                    if (!hResult.SUCCEEDED())
                        throw hResult.ToCOMException("TaskDialogIndirect failure.");
                }
                finally
                {
                    pTaskConfig.pButtons = IntPtr.Zero;
                    pTaskConfig.cButtons = 0;
                    pTaskConfig.pRadioButtons = IntPtr.Zero;
                    pTaskConfig.cRadioButtons = 0;
                }
            }

            return Tuple.Create(nButton, nRadioButton, bVerificationFlagChecked);
        }
#endif
    }
}